<?php
    /*
        @desc Dispatcher Class, handling url dispatch
        @author xq262144
    */
	class Dispatcher
    {
        private static $initialized = false;
    	private static $routes = null;
        private static $pathInfo = array();
        
        private static function _init()
        {
            if ( self::$initialized ) return false;
            else self::$initialized = true;

        	global $route;
            
        	self::$routes = $route;
            self::_initPathInfo();
        }

        private static function _redirect()
        {
            if ( isset( self::$routes['redirect'] ) && self::$routes['redirect'] ) {
                $url = self::getRebuildURL();
    
                header( 'Location: ' . $url );
                die;
            }
        }
        
        private static function _initPathInfo()
        {
        	$path = isset( $_SERVER['PATH_INFO'] ) ? $_SERVER['PATH_INFO'] : null;
            $path = explode( '/', $path );
            if ( is_array( $path ) && count( $path ) ) {
            	array_splice( $path, 0, 1 );
            } else {
            	$path = null;
            }
            
            //alias routes
            if ( is_array( $path ) ) {
            	array_walk( $path, create_function( '&$value', '$value = trim( $value );' ) );
                $path = array_filter( $path, create_function( '$value', 'return !empty( $value );' ) );
                $path = array_values( $path );
            }
            
            //path dispatching
            if ( !empty( $path ) ) {
            	//route dispatch
               	self::$pathInfo['class'] = $path[0];
                self::$pathInfo['method'] = empty( $path[1] ) ? 'index' : $path[1];
                self::$pathInfo['params'] = count( $path ) > 2 ? array_slice( $path, 2 ) : null;
            } else {
            	//default route
                self::$pathInfo = isset( self::$routes['default'] ) ? self::$routes['default'] : 
                	array( 
                    	'class' => 'default',
                    	'method' => 'index',
                        'params' => null
                    );
               	self::$pathInfo['params'] = null;
                self::_redirect();
            }
        }
        
        private static function _alias()
        {
        	if ( isset( self::$routes['alias'] ) && !empty( self::$routes['alias']['keys'] ) ) {
            	if ( false !== ( $offset = array_search( 
                	array( 
                		'class' => self::$pathInfo['class'],
                    	'method' => self::$pathInfo['method']
                        ),
                    self::$routes['alias']['keys']
                    ) ) )
                {
                	//searched
                    $path = self::$routes['alias']['values'][$offset];
					self::$pathInfo['class'] = $path['class'];
                    self::$pathInfo['method'] = $path['method'];

                    //route params aliasing
                    if ( isset( $path['params'] ) ) {
                        if ( empty( self::$pathInfo['params'] ) || !is_array( self::$pathInfo['params'] ) ) {
                            self::$pathInfo['params'] = array( );
                        }
                        //prepend info for aliasing
                        array_splice( self::$pathInfo['params'], 0, 0, $path['params'] );
                    }

                    self::_redirect();
                }
            }
        }
        
        private static function _dispatch()
        {
        	//real dispatch function, dispatch url to selected class and method
            if ( empty( self::$pathInfo ) ) {
            	return false;
            } else {
            	//app load
                if ( function_exists( 'load_app' ) ) {
                    if ( 0 === strncasecmp( self::$pathInfo['class'], '_', 1 ) ) {
                        //inner app
                        //won't dispatch
                        trigger_error( "App " . self::$pathInfo['class'] . " is a inner class that could not be loaded!", E_USER_ERROR );
                        return false;
                    }

                	if ( $instance = load_app( self::$pathInfo['class'] ) ) {
                        if ( $instance instanceof PageBase ) {
							if ( method_exists( $instance, self::$pathInfo['method'] ) ) {
                            	//dispatched
                            	call_user_func_array( array( $instance,  self::$pathInfo['method'] ), self::$pathInfo['params'] );
                            } else {
                            	trigger_error( "App " . self::$pathInfo['class'] . " have no " . self::$pathInfo['method'] . " method", E_USER_ERROR );
                            }
						} else {
                        	trigger_error( "App " . self::$pathInfo['class'] . " is not valid application!", E_USER_ERROR );
                        }
                    } else {
                    	trigger_error( "App " . self::$pathInfo['class'] . " loading failed, may not exist!", E_USER_ERROR );
                    }
                } else {
                	trigger_error( 'App loading utility could not be found!', E_USER_ERROR );
                }
            }
        }
        
        public static function dispatch()
        {
        	self::_init();
            self::_alias();
            self::_dispatch();
        }
        
        public static function getPathInfo()
        {
            self::_init();
        	return self::$pathInfo;
        }
        
        public static function getRoutesInfo()
        {
            self::_init();
        	return self::$routes;
        }
        
        public static function getRequestedURL()
        {
            self::_init();
        	return 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
        }

        public static function getRebuildURL()
        {
            self::_init();
            return URL_BASE . self::$pathInfo['class'] . '/' . self::$pathInfo['method'] . '/' . 
                implode( '/', is_array( self::$pathInfo['params'] ) ? self::$pathInfo['params'] : array( ) );
        }
    }